package com.wh.basic.jwt;//JWT密钥的解析和加密工具类

import com.alibaba.fastjson.JSONObject;
import com.wh.basic.vo.tokenVersion;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;
import org.joda.time.DateTime;

import javax.crypto.Cipher;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.UUID;

/**
 * JWT 密钥的解析和加密 工具类
 */
public class JwtTokenUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    private static String createJTI() {
        return Base64.encodeBase64String(UUID.randomUUID().toString().getBytes());
    }

    /**
     * RSA公钥加密
     *
     * @param obj             被加密数据
     * @param secretPublicKey 加密公钥
     * @return 密文
     */
    private static String encryptByPublicKey(Object obj, PublicKey secretPublicKey) {
        try {
            //把数据序列化为字符串
            String jsonStr = JSONObject.toJSONString(obj);
            //如果没有加密公钥，则不加密，直接返回
            if (secretPublicKey == null) {
                return jsonStr;
            }
            //RSA加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, secretPublicKey);
            String outStr = Base64.encodeBase64String(cipher.doFinal(jsonStr.getBytes("UTF-8")));
            return outStr;
        } catch (Exception e) {
            throw new RuntimeException("JWT数据密钥与本地数据不匹配");
        }
    }

    /**
     * RSA私钥解密
     *
     * @param str              要解密字符串
     * @param secretPrivateKey 解密私钥
     * @return 明文Json
     */
    private static String decryptByPrivateKey(String str, PrivateKey secretPrivateKey) {
        try {
            if (secretPrivateKey == null) {
                //如果没有解密的私钥，则不需要解密，直接返回
                return str;
            }
            //开始解密
            //1.解码加密后的字符串(加密字符串时被Base64加密过的)
            byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
            //2.RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, secretPrivateKey);
            String outStr = new String(cipher.doFinal(inputByte));
            return outStr;
        } catch (Exception e) {
            throw new RuntimeException("JWT数据密钥与本地数据不匹配");
        }
    }

    /**
     * 生成JWT的token（数据不加密）
     *
     * @param obj            载荷中的数据
     * @param signPrivateKey 签名私钥
     * @param expire         过期时间，单位秒
     * @return JWT
     */
    public static String generateTokenExpire(Object obj, PrivateKey signPrivateKey, int expire,String version) {
        return generateTokenExpire(obj, signPrivateKey, expire, null,version);
    }

    /**
     * 生成JWT的token（数据加密）
     *
     * @param obj             载荷中的数据
     * @param signPrivateKey  签名私钥
     * @param expire          过期时间，单位秒
     * @param secretPublicKey 加密公钥
     * @return JWT
     */
    public static String generateTokenExpire(Object obj, PrivateKey signPrivateKey, int expire, PublicKey secretPublicKey,String version) {
        //对数据进行加密处理
        String str = encryptByPublicKey(obj, secretPublicKey);
        //开始封装jwt（封装数据，私钥签名）
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, str)       //填充数据
                .claim("version",version)
                .setId(createJTI())                     //设置ID（无意义）
                .setExpiration(DateTime.now().plusSeconds(expire).toDate())     //设置过期时间
                .signWith(SignatureAlgorithm.RS256, signPrivateKey)             //使用私钥进行签名
                .compact();
    }

    /**
     * 获取token中的载荷信息（包括核心用户数据）
     *
     * @param token         用户请求中的令牌
     * @param signPublicKey 验签公钥
     * @param kernelType    核心数据的类型
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String version,String token, PublicKey signPublicKey, Class<T> kernelType) {
        return getInfoFromToken(version,token, signPublicKey, kernelType, null);
    }

    /**
     * 获取token中的载荷信息（包括核心用户数据）
     *
     * @param token            用户请求中的令牌
     * @param signPublicKey    验签公钥
     * @param kernelType       核心数据的类型
     * @param secretPrivateKey 解密公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String version,String token, PublicKey signPublicKey, Class<T> kernelType, PrivateKey secretPrivateKey) {
        //1.解析JWT的token
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signPublicKey).parseClaimsJws(token);
        //2.获取JWT中封装的数据
        Claims body = claimsJws.getBody();
        //3.解析JWT中载荷数据
        Payload<T> claims = new Payload<>();
        // 提取和验证版本号
        String tokenVersion = body.get("version", String.class);
        //3.1 封装Id
        claims.setId(body.getId());
        //3.2 封装核心数据（核心数据需要解密）
        //① 获取数据
        String bodyStr = body.get(JWT_PAYLOAD_USER_KEY).toString();
        //② 数据解密
        String json = decryptByPrivateKey(bodyStr, secretPrivateKey);
        //③ 把解密后的核心数据转换为对象
        T t = JSONObject.parseObject(json, kernelType);
        //④ 封装核心数据
        claims.setKernelData(t);
        //3.3 封装过期时间
        claims.setExpiration(body.getExpiration());
        //4封装token版本号
        claims.setVersion(tokenVersion);
        //返回数据
        return claims;
    }

    /**
     * 获取token中的载荷信息（不包括用户数据）
     *
     * @param token         用户请求中的令牌
     * @param signPublicKey 验签公钥
     * @return 用户信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey signPublicKey) {
        //1.解析JWT的token
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signPublicKey).parseClaimsJws(token);
        //2.获取JWT中封装的数据
        Claims body = claimsJws.getBody();
        //3.解析JWT中载荷数据
        Payload<T> claims = new Payload<>();
        // 提取和验证版本号
        String tokenVersion = body.get("version", String.class);
        //3.1 封装Id
        claims.setId(body.getId());
        //3.2 封装过期时间
        claims.setExpiration(body.getExpiration());
        //3.3封装token版本号
        claims.setVersion(tokenVersion);
        return claims;
    }

    public static void main(String[] args) throws Exception {
        // 1. 非加密Jwt验证
        // 1.1 生成Jwt

        try (InputStream privateKeyStream = JwtTokenUtils.class.getClassLoader().getResourceAsStream("auth_rsa.pri");
             InputStream publicKeyStream = JwtTokenUtils.class.getClassLoader().getResourceAsStream("auth_rsa.pub")) {

            if (privateKeyStream == null || publicKeyStream == null) {
                throw new FileNotFoundException("Private or public key file not found.");
            }
            //封装token版本
            String version= tokenVersion.TokenVersion;
            PrivateKey privateKey = RsaUtils.getPrivateKey(privateKeyStream);
            String token = generateTokenExpire(new User(1L, "zs"), privateKey, 10,version);

            // 1.2 解析jwt
            PublicKey publicKey = RsaUtils.getPublicKey(publicKeyStream);
            Payload<User> payload = getInfoFromToken(version,token, publicKey, User.class);


            // 等待一段时间，模拟超时
            Thread.sleep(11000);

            // 超时后继续解析
            payload = getInfoFromToken(version,token, publicKey, User.class);

        }
    }
    }